﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using System.Web.Razor.Generator;
using System.Web.Routing;
using Nop.Admin.Models.Invoice;
using Nop.Core;
using Nop.Core.Data;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Logistics;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Payments;
using Nop.Core.Domain.Shipping;
using Nop.Core.Domain.Stores;
using Nop.Core.Log;
using Nop.Services.Catalog;
using Nop.Services.Common;
using Nop.Services.Customers;
using Nop.Services.Helpers;
using Nop.Services.Localization;
using Nop.Services.Logistics;
using Nop.Services.Messages;
using Nop.Services.Orders;
using Nop.Services.Payments;
using Nop.Services.Security;
using Nop.Services.Stores;
using Nop.Web.Framework;
using Nop.Web.Framework.Controllers;
using Telerik.Web.Mvc;

namespace Nop.Admin.Controllers
{
    [AdminAuthorize]
    public class InvoiceController : BaseNopController
    {

        #region Fields
        private readonly IOrderService _orderService;
        private readonly IOrderReportService _orderReportService;
        private readonly IDateTimeHelper _dateTimeHelper;
        private readonly IPriceFormatter _priceFormatter;
        private readonly ILocalizationService _localizationService;
        private readonly IWorkContext _workContext;
        private readonly IPdfService _pdfService;
        private readonly IPermissionService _permissionService;
        private readonly IStoreService _storeService;
        private readonly IInvoiceService _invoiceService;
        private readonly ILogisticsService _logisticsService;
        private readonly IWorkflowMessageService _workflowService;
        private readonly ICustomerService _customerService;
        private readonly IPaymentService _paymentService;

        private readonly InvoiceSettings _invoiceSettings;
        private readonly DateTimeSettings _dateTimeSettings;
        private readonly IProductService _productService;

        private ILog _logService = LogManager.GetInstance().GetILog();

        #endregion

        public InvoiceController(IInvoiceService invoiceService, IOrderService orderService,
            IPermissionService permissionService, IStoreService storeService,
            ILocalizationService localizationService, IPdfService pdfService,
            IWorkflowMessageService workflowService, ILogisticsService logisticsService,
            ICustomerService customerService,
            IOrderReportService orderReportService, IPriceFormatter priceFormatter,
            InvoiceSettings invoiceSettings, DateTimeSettings dateTimeSettings,
            IDateTimeHelper dateTimeHelper, IWorkContext workContext,
            IRepository<District> districtRepository, IProductService productService,
            IPaymentService paymentService)
        {
            this._invoiceService = invoiceService;
            this._orderService = orderService;
            this._storeService = storeService;
            this._permissionService = permissionService;
            this._localizationService = localizationService;
            this._orderReportService = orderReportService;
            this._pdfService = pdfService;
            this._workflowService = workflowService;
            this._customerService = customerService;
            this._logisticsService = logisticsService;
            this._invoiceSettings = invoiceSettings;
            this._dateTimeSettings = dateTimeSettings;
            this._priceFormatter = priceFormatter;
            this._dateTimeHelper = dateTimeHelper;
            this._workContext = workContext;
            this._paymentService = paymentService;
            this._productService = productService;
        }

        #region Invoice list

        public ActionResult Index()
        {
            return RedirectToAction("List");
        }

        public ActionResult List()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
            {
                return AccessDeniedView();
            }

            var model = new InvoiceListModel();

            model.AvailableStatuses = InvoiceStatus.Published.ToSelectList(false).ToList();
            model.AvailableStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            model.AvailableOrderKinds = OrderKind.Express.ToSelectList(false).ToList();
            model.AvailableOrderKinds.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });

            model.AvailableDistricts = _logisticsService.GetAllDistricts()
                .Select(x => new SelectListItem() { Value = x.Id.ToString(), Text = x.Name, Selected = false })
                .ToList();
            model.AvailableDistricts.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });

            model.StoreId = Store.AllStoreReservedId;
            return View(model);
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult _InvoiceList(GridCommand command, InvoiceListModel model)
        {
            DateTime? startDateValue = (model.StartDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, _dateTimeHelper.CurrentTimeZone);

            DateTime? endDateValue = (model.EndDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1);

            InvoiceStatus? status = model.InvoiceStatusId > 0 ? (InvoiceStatus?)(model.InvoiceStatusId) : null;
            OrderKind? orderKind = model.OrderKindId > 0 ? (OrderKind?)(model.OrderKindId) : null;
            int? districtId = model.DistrictId > 0 ? (int?)(model.DistrictId) : null;

            var invoices = _invoiceService.SearchInvoices(
               null,
               startDateValue,
               endDateValue,
               status,
               model.Email,
               model.BillingEmail,
               model.InvoiceGuid,
               null,
               model.Name,
               orderKind,
               districtId,
               model.StoreId,
               command.Page - 1,
               command.PageSize);

            var gridModel = new GridModel<InvoiceModel>
            {
                Data = invoices.Select(x => PrepareInvoiceModel(x, false)),
                Total = invoices.TotalCount
            };

            var report = _invoiceService.GetInvoiceReport(
               startDateValue,
               endDateValue,
               status,
               model.Email,
               model.BillingEmail,
               model.InvoiceGuid,
               null,
               model.Name,
               orderKind,
               districtId,
               model.StoreId);

            gridModel.Aggregates = new
            {
                aggregatortax = _priceFormatter.FormatPrice(report.SumTax, true, false),
                aggregatortotal = _priceFormatter.FormatPrice(report.SumInvoices, true, false)
            };

            return new JsonResult
            {
                Data = gridModel
            };
        }

        [HttpPost, ActionName("List")]
        [FormValueRequired("go-to-invoice-by-number")]
        public ActionResult GoToInvoiceNumber(InvoiceListModel model)
        {
            if (String.IsNullOrEmpty(model.GoDirectlyToNumber))
            {
                return List();
            }

            var invoice = _invoiceService.GetInvoiceByNumber(model.GoDirectlyToNumber);
            if (invoice != null)
                return RedirectToAction("Edit", "Invoice", new { id = invoice.Id });
            else
            {
                ErrorNotification(String.Format("Invoice # {0} not found", model.GoDirectlyToNumber));
                return List();
            }
        }

        #endregion

        #region Order list

        public ActionResult OrderList(string orderIds, int orderKindId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)
                || _workContext.CurrentCustomer.StoreId != Store.AllStoreReservedId)
            {
                return AccessDeniedView();
            }

            var model = new InvoiceOrderListModel();
            model.AvailableOrderStatuses = OrderStatus.Pending.ToSelectList(false).ToList();
            model.AvailableOrderStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });

            if (orderKindId != (int)OrderKind.Commerce)
            {
                model.AvailablePaymentStatuses = PaymentStatus.Pending.ToSelectList(false).ToList();
                model.AvailablePaymentStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });

                model.AvailableDistricts = _logisticsService.GetAllDistricts()
                .Select(x => new SelectListItem() { Value = x.Id.ToString(), Text = x.Name, Selected = false })
                .ToList();
                model.AvailableDistricts.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            }
            model.AvailableShippingStatuses = ShippingStatus.NotYetShipped.ToSelectList(false).ToList();
            model.AvailableShippingStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });

            foreach (var tzi in _dateTimeHelper.GetSystemTimeZones())
                model.AvailableTimeZones.Add(new SelectListItem() { Text = tzi.DisplayName, Value = tzi.Id, Selected = (tzi.Id == _dateTimeSettings.DefaultStoreTimeZoneId) });

            // Useful only during invoice creation:
            // Captures the order Ids that are already part of an invoice currently being created.
            model.InvoiceOrderIds = orderIds;

            if (orderKindId == 10)
            {
                model.SearchStoreId = 23;
            }
            else
            {
                var storeFirst = _storeService.GetAllStores().FirstOrDefault(d => d.Id != 1);
                if (storeFirst != null)
                    model.SearchStoreId = storeFirst.Id;
            }
            model.OrderKindId = orderKindId;
            model.SearchPaidRefundOrders = false;
            return View(model);
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult _OrderList(GridCommand command, InvoiceOrderListModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)
                || _workContext.CurrentCustomer.StoreId != Store.AllStoreReservedId)
            {
                return AccessDeniedView();
            }

            var timeZoneInfo = _dateTimeHelper.FindTimeZoneById(model.TimeZoneId);

            DateTime? startDateValue = (model.StartDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, timeZoneInfo);

            DateTime? endDateValue = (model.EndDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, timeZoneInfo).AddDays(1);

            OrderStatus? orderStatus = model.OrderStatusId > 0 ? (OrderStatus?)(model.OrderStatusId) : null;
            ShippingStatus? shippingStatus = model.ShippingStatusId > 0 ? (ShippingStatus?)(model.ShippingStatusId) : null;
            OrderKind? orderKind = model.OrderKindId > 0 ? (OrderKind?)(model.OrderKindId) : null;
            var pss = model.PaymentStatusId > 0 ? new PaymentStatus[] { (PaymentStatus)(model.PaymentStatusId) } : null;
            int? districtId = model.DistrictId > 0 ? (int?)model.DistrictId : null;
            bool excludeInvoiceAssigned = !model.SearchAllOrders;
            bool includePaidRefundOrdersOnly = model.SearchPaidRefundOrders;

            // this captures orders that are already part of a being-created invoice.
            // Thus, should not search for these orders again!
            // (i.e. Exclude them so they won't be available to users 
            // Not allowed to select two checkboxs together
            int[] invoiceOrderIds = null;
            if (model.InvoiceOrderIds != null && model.InvoiceOrderIds.Length > 0)
            {
                if (includePaidRefundOrdersOnly)
                    throw new ArgumentException("You cannot select two checkboxs together");
                invoiceOrderIds = model.InvoiceOrderIds
                    .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(x => Convert.ToInt32(x))
                    .ToArray();
            }
            if (orderKind == OrderKind.Express)
            {
                model.SearchStoreId = 23;
            }
            var gridModel = new GridModel<InvoiceOrderModel>();
            if (orderKind == OrderKind.Commerce)
            {
                if (model.SearchStoreId > Store.AllStoreReservedId)
                {
                    if (includePaidRefundOrdersOnly == true)
                    {
                        pss = new PaymentStatus[] { PaymentStatus.Paid, PaymentStatus.PartiallyRefunded, PaymentStatus.Refunded };
                    }
                }
                //else
                //{
                //    return new JsonResult
                //    {
                //        Data = gridModel
                //    };
                //}
            }

            //load orders
            var orders = _orderService.SearchOrders(
                startDateValue,
                endDateValue,
                orderStatus,
                pss,
                shippingStatus,
                model.CustomerEmail,
                model.OrderGuid,
                command.Page - 1,
                command.PageSize,
                invoiceOrderIds,
                model.SearchStoreId,
                districtId,
                orderKind,
                excludeInvoiceAssigned);

            gridModel = new GridModel<InvoiceOrderModel>
            {
                Data = orders.Select(x => PrepareOrderModel(x, timeZoneInfo)),
                Total = orders.TotalCount
            };

            //summary report for all orders
            //implemented as a workaround described here: http://www.telerik.com/community/forums/aspnet-mvc/grid/gridmodel-aggregates-how-to-use.aspx

            var sumOrders = SearchOrders(startDateValue, endDateValue, orderStatus, pss, shippingStatus, model,
                invoiceOrderIds, districtId, orderKind, excludeInvoiceAssigned);

            //var reportSummary = _orderReportService.GetOrderAverageReportLine
            //    (orderStatus, pss, shippingStatus, startDateValue, endDateValue, model.CustomerEmail, model.SearchStoreId, false, districtId, (int?)orderKind);

            var reportSummary = _orderReportService.GetOrderAverageReportLineBySerachOrder(sumOrders);

            //var profit = _orderReportService.ProfitReport
            //(orderStatus, pss, shippingStatus, startDateValue, endDateValue, model.CustomerEmail, model.SearchStoreId, districtId);
            var aggregator = new InvoiceOrderModel()
            {
                //aggregatorprofit = _priceFormatter.FormatPrice(profit, true, false),
                aggregatorsubtotalexcltax = _priceFormatter.FormatPrice(reportSummary.SumOrderSubtotalExclTax, true, false),
                aggregatorfuelsurcharge = _priceFormatter.FormatPrice(reportSummary.SumFuelSurcharge, true, false),
                aggregatortax = _priceFormatter.FormatPrice(reportSummary.SumTax, true, false),
                aggregatortotal = _priceFormatter.FormatPrice(reportSummary.SumOrders, true, false)
            };
            gridModel.Aggregates = aggregator;
            return new JsonResult
            {
                Data = gridModel
            };
        }

        private IList<Order> SearchOrders(DateTime? startDateValue, DateTime? endDateValue, OrderStatus? orderStatus, PaymentStatus[] pss, ShippingStatus? shippingStatus, InvoiceOrderListModel model, int[] invoiceOrderIds, int? districtId, OrderKind? orderKind, bool excludeInvoiceAssigned)
        {
            var orderTemps = _orderService.SearchOrders(startDateValue, endDateValue, orderStatus, pss, shippingStatus,
                model.CustomerEmail, model.OrderGuid, invoiceOrderIds, 0, districtId, orderKind, excludeInvoiceAssigned);
            if (orderTemps != null)
            {
                orderTemps = orderTemps.Where(d => d.BoundOrderId == null).ToList();
            }
            return orderTemps;
        }

        [HttpPost]
        [FormValueRequired("generateinvoice-selected")]
        public ActionResult OrderList(string selectedIds, DateTime? start, DateTime? end, InvoiceOrderListModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
            {
                return AccessDeniedView();
            }
            //53366,53348
            var combinedOrderIds = String.Join(",", model.InvoiceOrderIds, selectedIds);

            // sort the combined order IDs to allow better presentation on GUI.
            if (combinedOrderIds != null && combinedOrderIds.Length > 0)
            {
                combinedOrderIds = String.Join(",",
                    combinedOrderIds
                        .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                        .Select(x => Convert.ToInt32(x))
                        .OrderByDescending(x => x));
            }
            //combinedOrderIds = "53366,53348";
            //combinedOrderIds = "53366";
            if (String.IsNullOrEmpty(combinedOrderIds))
            {
                ErrorNotification("Select at least one order to create an invoice.");
                return RedirectToAction("List");
            }

            int? storeId = null;
            if (model.OrderKindId == (int)OrderKind.Commerce)
            {
                var store = _storeService.GetStoreById(model.SearchStoreId);
                if (store == null)
                    throw new ArgumentNullException("store");
                storeId = store.Id;
            }
            var invoiceModel = CreateInvoiceModel(combinedOrderIds, start, end, storeId);
            invoiceModel.OrderKindId = model.OrderKindId;
            // clear modelState cached values
            ModelState.Clear();

            return View("Create", invoiceModel);
        }

        #endregion

        #region Create

        public ActionResult Create()
        {
            return RedirectToAction("OrderList");
        }

        [HttpPost]
        [FormValueRequired("save-continue")]
        public ActionResult Create(InvoiceModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    var customer = _customerService.GetCustomerById(model.CustomerId);
                    if (customer == null || model.OrderIds == null)
                    {
                        ErrorNotification(string.Format("{0} is not found", model.CustomerId));
                        return RedirectToAction("OrderList", new { orderKindId = model.OrderKindId });
                        //return RedirectToAction("Edit", new { errorMessage = string.Format("{0} is not found", model.CustomerId) });
                    }

                    var ids = model.OrderIds
                        .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                        .Select(x => Convert.ToInt32(x))
                        .ToArray();

                    var invoice = _invoiceService.CreateInvoice(ids, model.Name, model.Comment, customer, model.DueDate ?? DateTime.Today.AddDays(_invoiceSettings.DueDateInDays), InvoiceStatus.Pending);

                    invoice.InvoiceNotes.Add(new InvoiceNote()
                    {
                        Note = string.Format(_localizationService.GetResource("admin.invoice.invoicecreate"), _workContext.CurrentCustomer.Email),
                        DisplayToCustomer = false,
                        CreatedOnUtc = DateTime.UtcNow
                    });

                    if (invoice.SubTotal <= 0)
                    {
                        ErrorNotification(string.Format("invoice.SubTotal ：{0} is less than 0", invoice.SubTotal));
                        return RedirectToAction("Edit", new { id = invoice.Id });
                    }

                    var paymentMethod = _paymentService.LoadPaymentMethodBySystemName("Payments.GlobalPayments");
                    if (paymentMethod == null)
                        throw new Exception("Payment method is null");

                    var orderList = _orderService.GetOrdersByIds(ids);

                    //vertify orderStatus=Processing and order's paymentStatus=Pending
                    if (VertifyIsLogisticMemeberAndOrderStatus(customer, orderList))
                    {
                        var creditCard = paymentMethod.GetCreditCard(0, GetStoreIdFromOrder(orderList), false, true);
                        if (creditCard == null)
                        {
                            return RedirectToAction("Edit", new { errorMessage = string.Format("{0} has no creditCard", customer.GetFullName()) });
                        }

                        string paymentMessage = "";
                        var paySuccess = paymentMethod.AddPaymentsTransaction(ids.ToList(), invoice.SubTotal, creditCard.Id, creditCard.Token, out paymentMessage);

                        if (!paySuccess)
                        {
                            return RedirectToAction("Edit", new { errorMessage = paymentMessage });
                        }

                        invoice.InvoiceStatus = InvoiceStatus.Paid;
                        _invoiceService.UpdateInvoice(invoice);

                        UpdateOrderPayStatus(orderList);
                    }

                    var invoiceId = invoice.Id;
                    return RedirectToAction("Edit", new { id = invoiceId });
                }
            }
            catch (Exception exc)
            {
                ErrorNotification(exc);
                return RedirectToAction("OrderList", new { orderKindId = model.OrderKindId });
            }
            return RedirectToAction("List");
        }

        private bool VertifyIsLogisticMemeberAndOrderStatus(Customer customer, IList<Order> orderList)
        {
            if (customer == null || orderList == null || orderList.Count <= 0) return false;

            try
            {
                var isMember = _permissionService.Authorize(StandardPermissionProvider.LogisticsPaymentExempt, customer);

                if (!isMember) return false;

                var vertifyFlag = VertifyOrderStatus(orderList);

                return vertifyFlag;

            }
            catch (Exception exception)
            {
                _logService.Error("Admin sales invoice --create invoice error; errorFunction:VertifyIsLogisticMemeberAndOrderStatus " + exception.ToString());

                return false;
            }
        }

        private bool VertifyOrderStatus(IList<Order> orderList)
        {
            if (orderList == null || orderList.Count <= 0) return false;

            foreach (var order in orderList)
            {
                if (order != null)
                {
                    if (order.OrderStatus != OrderStatus.Processing || order.PaymentStatusId != (int)PaymentStatus.Pending)
                    {
                        _logService.Info("Admin sales invoice --create invoice error;VertifyOrderStatus,vertify false " + string.Format("{0} orderStatus is {1} ,paymentStatus is {2}", order.Id, order.OrderStatus, order.PaymentStatusId));

                        return false;
                    }
                }
                else
                {
                    _logService.Info("Admin sales invoice --create invoice error;VertifyOrderStatus " + string.Format("{0} orderStatus is {1} ,paymentStatus is {2}", order.Id, order.OrderStatus, order.PaymentStatusId));

                    return false;
                }
            }
            return true;
        }

        private int GetStoreIdFromOrder(IList<Order> orderList)
        {
            if (orderList == null || orderList.Count <= 0) return 0;

            var storeId = orderList.Select(d => d.StoreId).FirstOrDefault();

            try
            {
                if (!storeId.HasValue)
                {
                    var orderProductVariant = orderList.FirstOrDefault().OrderProductVariants.FirstOrDefault();
                    if (orderProductVariant != null)
                    {
                        var product = _productService.GetProductById(orderProductVariant.ProductVariant.ProductId);

                        storeId = product == null ? 0 : product.StoreId;
                    }
                }
            }
            catch (Exception exception)
            {
                _logService.Error("GetStoreIdFromOrder error: " + exception.ToString());
            }

            return storeId.HasValue && storeId.Value > 0 ? (int)storeId.Value : 23;
        }

        private void UpdateOrderPayStatus(IList<Order> orderList)
        {
            if (orderList == null || orderList.Count <= 0) return;

            foreach (var item in orderList)
            {
                if (item.PaymentStatusId == (int)PaymentStatus.Paid) continue;

                item.PaymentStatusId = (int)PaymentStatus.Paid;
                _orderService.UpdateOrder(item);
            }
        }

        [HttpPost, ActionName("Create")]
        [FormValueRequired("remove-orders")]
        public ActionResult RemoveOrders(InvoiceModel model, string selectedIds)
        {
            if (ModelState.IsValid)
            {
                Int32[] orderIds = null;
                if (model.OrderIds != null)
                {
                    orderIds = model.OrderIds
                        .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                        .Select(x => Convert.ToInt32(x))
                        .ToArray();
                }

                Int32[] removedOrderIds = null;
                if (selectedIds != null)
                {
                    removedOrderIds = selectedIds
                        .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                        .Select(x => Convert.ToInt32(x))
                        .ToArray();
                }

                string remainingOrderIds = model.OrderIds;
                if (orderIds.Length > 0 && removedOrderIds.Length > 0)
                {
                    orderIds = orderIds.Where(o => !removedOrderIds.Contains(o)).ToArray();

                    remainingOrderIds = String.Join(",", orderIds);
                }
                //Avoid error when no remaining Order id
                if (string.IsNullOrEmpty(remainingOrderIds))
                {
                    return RedirectToAction("OrderList", new { orderKindId = model.OrderKindId });
                }

                var newModel = CreateInvoiceModel(remainingOrderIds, model.StartDate, model.EndDate);

                // clear modelState cached values
                var msKeys = new string[ModelState.Keys.Count];
                ModelState.Keys.CopyTo(msKeys, 0);
                foreach (var msKey in msKeys)
                {
                    ModelState.Remove(msKey);
                }

                return View(newModel);
            }

            return View(model);
        }

        [HttpPost, ActionName("Create")]
        [FormValueRequired("add-orders")]
        public ActionResult AddOrders(InvoiceModel model)
        {
            if (ModelState.IsValid)
            {
                return RedirectToAction("OrderList", new { orderIds = model.OrderIds, orderKindId = model.OrderKindId });
            }

            return View(model);
        }

        public ActionResult BulkCreate()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)
                || _workContext.CurrentCustomer.StoreId != Store.AllStoreReservedId)
            {
                return AccessDeniedView();
            }

            var model = new InvoiceOrderListModel();

            model.AvailableDistricts = _logisticsService.GetAllDistricts()
                .Select(x => new SelectListItem() { Value = x.Id.ToString(), Text = x.Name, Selected = false })
                .ToList();
            model.AvailableDistricts.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Logistics.Common.SelectDistrict"), Value = "0" });

            foreach (var tzi in _dateTimeHelper.GetSystemTimeZones())
                model.AvailableTimeZones.Add(new SelectListItem() { Text = tzi.DisplayName, Value = tzi.Id, Selected = (tzi.Id == _dateTimeSettings.DefaultStoreTimeZoneId) });

            return View(model);
        }

        [HttpPost]
        public ActionResult BulkCreate(InvoiceOrderListModel model)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);
            if (curManagedStoreId == 0 || _workContext.CurrentCustomer.StoreId != Store.AllStoreReservedId)
            {
                return AccessDeniedView();
            }

            if (model.TimeZoneId == null || model.StartDate == null || model.EndDate == null || model.DistrictId == 0)
            {
                ErrorNotification("Every item should have value to create an invoice.");
                return RedirectToAction("BulkCreate");
            }

            var timeZoneInfo = _dateTimeHelper.FindTimeZoneById(model.TimeZoneId);
            DateTime startDateValue = (DateTime)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, timeZoneInfo);
            DateTime endDateValue = (DateTime)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, timeZoneInfo).AddDays(1);
            var dueDate = DateTime.Now.AddDays(_invoiceSettings.DueDateInDays);

            //TODO: Only create express order invoices for now
            var orderKind = OrderKind.Express;
            //Name is like "For the Period of: {0} - {1}"
            var name = String.Format(_localizationService.GetResource("Admin.Invoice.PeriodInvoiceName"),
                model.StartDate.Value.ToShortDateString(),
                model.EndDate.Value.ToShortDateString());

            try
            {
                _invoiceService.BulkCreateInvoices(startDateValue, endDateValue, model.DistrictId, name, dueDate, orderKind);
            }

            catch (Exception exc)
            {
                ErrorNotification(exc);
                return RedirectToAction("List");
            }

            return RedirectToAction("List");
        }
        #endregion

        #region Edit

        public ActionResult Edit(int? id, string errorMessage)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
            {
                return AccessDeniedView();
            }
            if (id == null && !string.IsNullOrEmpty(errorMessage))
            {
                ViewBag.ErrorMessage = errorMessage;
                return View();
            }
            var invoice = _invoiceService.GetInvoiceById(id.Value);
            if (invoice == null)
                //No order found with the specified id
                return RedirectToAction("List");

            return View(PrepareInvoiceModel(invoice, true));
        }

        [HttpPost, ParameterBasedOnFormNameAttribute("save-continue", "continueEditing")]
        [FormValueRequired("save", "save-continue")]
        public ActionResult Edit(InvoiceModel model, bool continueEditing)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
            {
                return AccessDeniedView();
            }

            var invoice = _invoiceService.GetInvoiceById(model.Id);
            if (invoice == null)
                //No invoice found with the specified id
                return RedirectToAction("List");

            if (ModelState.IsValid)
            {
                try
                {
                    invoice.Name = model.Name;
                    invoice.Comment = model.Comment;
                    invoice.CustomerId = model.CustomerId;
                    if (invoice.InvoiceStatusId != model.InvoiceStatusId)
                    {
                        invoice.InvoiceNotes.Add(new InvoiceNote()
                        {
                            Note = string.Format(_localizationService.GetResource("admin.invoice.invoicechange"), (InvoiceStatus)model.InvoiceStatusId, _workContext.CurrentCustomer.Email),
                            DisplayToCustomer = false,
                            CreatedOnUtc = DateTime.UtcNow
                        });
                    }
                    else
                    {
                        invoice.InvoiceNotes.Add(new InvoiceNote()
                        {
                            Note = string.Format(_localizationService.GetResource("admin.invoice.invoiceupdate"), _workContext.CurrentCustomer.Email),
                            DisplayToCustomer = false,
                            CreatedOnUtc = DateTime.UtcNow
                        });
                    }

                    if (_workContext.CurrentCustomer.IsAdmin())
                    {
                        invoice.InvoiceStatusId = model.InvoiceStatusId;
                        invoice.InvoiceNumber = model.InvoiceNumber;
                        invoice.PaidDateUtc = DateTime.UtcNow;
                    }

                    invoice.SubTotal = model.SubTotal;
                    invoice.FuelSurcharge = model.FuelSurcharge;
                    invoice.Tax = model.Tax;
                    invoice.Discount = model.Discount;
                    invoice.Total = model.Total;
                    invoice.DueDate = model.DueDate;

                    _invoiceService.UpdateInvoice(invoice);
                    return continueEditing ? RedirectToAction("Edit", invoice.Id) : RedirectToAction("List");
                }
                catch (Exception exc)
                {
                    ErrorNotification(exc.Message, false);
                }
            }

            return View(model);
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("setordersaspaid")]
        public ActionResult SetOrdersAsPaid(int id, int paymentMethodId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var invoice = _invoiceService.GetInvoiceById(id);
            if (invoice == null)
                //No invoice found with the specified id
                return RedirectToAction("List");

            InvoiceStatus paidStatusByPaymentMethod = (InvoiceStatus)paymentMethodId;
            if (paidStatusByPaymentMethod != InvoiceStatus.Paid &&
                paidStatusByPaymentMethod != InvoiceStatus.PaidByCheck &&
                paidStatusByPaymentMethod != InvoiceStatus.PaidByCreditCard)
            {
                ErrorNotification(String.Format("Invoice status cannot be changed to {0}.", paidStatusByPaymentMethod));
                return RedirectToAction("Edit", new { id = invoice.Id });
            }

            try
            {
                foreach (var order in invoice.Orders)
                {
                    order.PaymentStatus = PaymentStatus.Paid;
                }
                invoice.InvoiceStatus = paidStatusByPaymentMethod;
                invoice.PaidDateUtc = DateTime.UtcNow;
                invoice.InvoiceNotes.Add(new InvoiceNote()
                {
                    Note = string.Format("Set order as paid: {0} by {1}.", paidStatusByPaymentMethod, _workContext.CurrentCustomer.Email),
                    DisplayToCustomer = false,
                    CreatedOnUtc = DateTime.UtcNow
                });
                _invoiceService.UpdateInvoice(invoice);

                if (_workflowService.SendExpressInvoicePaidNotification(
                    invoice,
                    invoice.Customer.LanguageId.HasValue ?
                    invoice.Customer.LanguageId.Value : _workContext.WorkingLanguage.Id) == 0)
                {
                    ErrorNotification("Invoice paid notification cannot be sent, but status updated successfully.");
                }

                SuccessNotification("The invoice is paid notification has been sent.");

                return RedirectToAction("Edit", new { id = invoice.Id });
            }
            catch (Exception exc)
            {
                //error
                ErrorNotification(exc, true);
                return RedirectToAction("Edit", new { id = invoice.Id });
            }
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("publish")]
        public ActionResult Publish(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var invoice = _invoiceService.GetInvoiceById(id);
            if (invoice == null)
                //No invoice found with the specified id
                return RedirectToAction("List");

            try
            {
                if (invoice.InvoiceStatus == InvoiceStatus.Pending)
                {
                    invoice.InvoiceStatus = InvoiceStatus.Published;
                }
                invoice.InvoiceNotes.Add(new InvoiceNote()
                {
                    Note = string.Format(_localizationService.GetResource("admin.invoice.invoicepublish"), _workContext.CurrentCustomer.Email),
                    DisplayToCustomer = false,
                    CreatedOnUtc = DateTime.UtcNow
                });

                _invoiceService.UpdateInvoice(invoice);

                if (invoice.InvoiceStatus == InvoiceStatus.Published)
                {
                    if (_workflowService.SendExpressInvoiceReadyNotification(
                        invoice,
                        invoice.Customer.LanguageId.HasValue ?
                            invoice.Customer.LanguageId.Value : _workContext.WorkingLanguage.Id) == 0)
                    {
                        ErrorNotification("Sending of the invoice notification has failed.");
                    }

                    SuccessNotification("The invoice notification has been sent.");
                }

                return RedirectToAction("Edit", new { id = invoice.Id });
            }
            catch (Exception exc)
            {
                //error
                ErrorNotification(exc, true);
                return RedirectToAction("Edit", new { id = invoice.Id });
            }
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("sendexpress")]
        public ActionResult SendExpressInvoiceNotification(int id)
        {

            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var invoice = _invoiceService.GetInvoiceById(id);
            if (invoice == null)
                //No invoice found with the specified id
                return RedirectToAction("List");

            try
            {
                if (invoice.InvoiceStatus == InvoiceStatus.Published)
                {
                    if (_workflowService.SendExpressInvoiceReadyNotification(
                        invoice,
                        invoice.Customer.LanguageId.HasValue ?
                            invoice.Customer.LanguageId.Value : _workContext.WorkingLanguage.Id) == 0)
                    {
                        ErrorNotification("Sending of the invoice notification has failed.");
                    }

                    SuccessNotification("The invoice notification has been sent.");
                }
                else
                {
                    ErrorNotification("The invoice state is not correct.");
                }

                return RedirectToAction("Edit", new { id = invoice.Id });
            }
            catch (Exception exc)
            {
                //error
                ErrorNotification(exc, true);
                return RedirectToAction("Edit", new { id = invoice.Id });
            }
        }

        #endregion
        #region Invoice notes

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult InvoiceNotesSelect(int invoiceId, GridCommand command)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var invoice = _invoiceService.GetInvoiceById(invoiceId);
            if (invoice == null)
                throw new ArgumentException("No invoice found with the specified id");

            //invoice notes
            var invoiceNoteModels = invoice.InvoiceNotes
                .OrderByDescending(x => x.CreatedOnUtc)
                .Select(x =>
                    {
                        var createdOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc);
                        return new InvoiceModel.InvoiceNote()
                        {
                            Id = x.Id,
                            InvoiceId = x.InvoiceId,
                            DisplayToCustomer = x.DisplayToCustomer,
                            Note = x.Note,
                            CreatedOn = createdOn,
                            CreatedOnStr = createdOn.ToString("g")
                        };
                    })
                .ToList();

            var model = new GridModel<InvoiceModel.InvoiceNote>
            {
                Data = invoiceNoteModels,
                Total = invoiceNoteModels.Count
            };

            return new JsonResult
            {
                Data = model
            };
        }

        [ValidateInput(false)]
        public ActionResult InvoiceNoteAdd(int invoiceId, bool displayToCustomer, string message)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var invoice = _invoiceService.GetInvoiceById(invoiceId);
            if (invoice == null)
                return Json(new { Result = false }, JsonRequestBehavior.AllowGet);

            var invoiceNote = new InvoiceNote()
            {
                DisplayToCustomer = displayToCustomer,
                Note = message + " by " + _workContext.CurrentCustomer.Email,
                CreatedOnUtc = DateTime.UtcNow,
            };
            invoice.InvoiceNotes.Add(invoiceNote);
            _invoiceService.UpdateInvoice(invoice);

            return Json(new { Result = true }, JsonRequestBehavior.AllowGet);
        }

        #endregion

        public ActionResult Delete(int id)
        {
            var invoice = _invoiceService.GetInvoiceById(id);
            invoice.InvoiceNotes.Add(new InvoiceNote()
            {
                Note = string.Format(_localizationService.GetResource("admin.invoice.invoicedelete"), _workContext.CurrentCustomer.Email),
                DisplayToCustomer = false,
                CreatedOnUtc = DateTime.UtcNow
            });
            _invoiceService.DeleteInvoice(invoice);
            return RedirectToAction("Edit", "Invoice", new { id = invoice.Id });
        }

        public ActionResult PdfInvoice(int invoiceId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var invoice = _invoiceService.GetInvoiceById(invoiceId);

            byte[] bytes = null;
            using (var stream = new MemoryStream())
            {
                _pdfService.PrintInvoiceToPdf(stream, invoice, _workContext.WorkingLanguage);
                bytes = stream.ToArray();
            }
            if (invoice.OrderKindId == (int)OrderKind.Commerce)
            {
                return File(bytes, "application/pdf", string.Format(_localizationService.GetResource("Admin.Invoice.PDFInvoice.CommerceInvoice.Title"), DateTime.Now.ToString("dd-M-yyyy__HH-mm-ss")));
            }

            return File(bytes, "application/pdf", String.Format(_localizationService.GetResource("Admin.Invoice.PDFInvoice.ExpressInvoice.Title"), DateTime.Now.ToString("dd-M-yyyy__HH-mm-ss")));
        }

        public ActionResult PdfInvoices(string selectedIds)
        {
            if (!ModelState.IsValid)
            {
                return RedirectToAction("List");
            }

            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders)
                || _workContext.CurrentCustomer.StoreId != Store.AllStoreReservedId)
            {
                return AccessDeniedView();
            }

            try
            {
                var invoices = new List<Invoice>();
                if (selectedIds != null)
                {
                    var ids = selectedIds
                        .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                        .Select(x => Convert.ToInt32(x))
                        .ToArray();
                    invoices.AddRange(_invoiceService.GetInvoicesByIds(ids));
                }

                // TODO: Max to be re-evaluated or to be handled in the future!
                const int maxItems = 100;
                int numItems = invoices.Count();
                if (numItems > maxItems)
                    throw new Exception(String.Format("{0} invoices requested is greater than the maximum limit of {1}",
                                                      numItems, maxItems));

                byte[] bytes = null;
                using (var stream = new MemoryStream())
                {
                    _pdfService.PrintInvoiceToPdf(stream, invoices, _workContext.WorkingLanguage);
                    bytes = stream.ToArray();
                }
                return File(bytes, "application/pdf", String.Format(_localizationService.GetResource("Admin.Invoice.PDFInvoice.ExpressInvoice.Title"), DateTime.Now.ToString("dd-M-yyyy__HH-mm-ss")));
            }
            catch (Exception exc)
            {
                ErrorNotification(exc);
                return RedirectToAction("List");
            }
        }

        [NonAction]
        protected InvoiceModel PrepareInvoiceModel(Invoice invoice, bool edit)
        {
            if (invoice == null)
                throw new ArgumentNullException("invoice");

            var createdOn = _dateTimeHelper.ConvertToUserTime(invoice.CreatedOnUtc, DateTimeKind.Utc);
            var model = new InvoiceModel()
            {
                Id = invoice.Id,
                InvoiceGuid = invoice.InvoiceGuid,
                InvoiceNumber = invoice.InvoiceNumber,
                CustomerId = invoice.CustomerId,
                CustomerEmail = invoice.Customer.Email,
                BillingEmail =   invoice.Customer.BillingAddress != null ? invoice.Customer.BillingAddress.Email : "",
                Name = invoice.Name,
                InvoiceStatus = invoice.InvoiceStatus.GetLocalizedEnum(_localizationService, _workContext),
                InvoiceStatusId = invoice.InvoiceStatusId,
                Comment = invoice.Comment,
                CreatedOn = createdOn,
                CreatedOnStr = createdOn.ToString("g"),
                SubTotal = invoice.SubTotal,
                FuelSurcharge = invoice.FuelSurcharge,
                Discount = invoice.Discount,
                Tax = invoice.Tax,
                Total = invoice.Total,
                DueDate = invoice.DueDate,
                TimeZoneId = invoice.TimeZoneId,
                OrderKindId = invoice.OrderKindId,
            };

            model.OrderKindStr = ((OrderKind)model.OrderKindId).ToString();
            var district = _logisticsService.GetDistrictById(invoice.DistrictId);
            model.District = district == null ? "Unknown" : district.Name;

            if (edit)
            {
                if (model.OrderKindId == (int)OrderKind.Commerce)
                {
                    var commerceOrders = new List<Order>();
                    model.AvailableCustomers = GetAvaliableCustomersFromOrders(commerceOrders, invoice.Customer);
                    model.OrderKindId = invoice.OrderKindId;
                    model.MonthlyCharge = invoice.MonthlyCharge;
                    model.StorageCharge = invoice.StorageCharge;
                    model.ServiceCharge = invoice.ServiceCharge;
                    model.TransactionProcessingCharge = invoice.TransactionProcessingCharge;
                }
                else
                {
                    model.AvailableCustomers = GetAvaliableCustomersFromOrders(invoice.Orders.ToList(), invoice.Customer);
                }

                foreach (var tzi in _dateTimeHelper.GetSystemTimeZones())
                    model.AvailableTimeZones.Add(new SelectListItem() { Text = tzi.DisplayName, Value = tzi.Id, Selected = (tzi.Id == invoice.TimeZoneId) });

                var timeZoneInfo = _dateTimeHelper.FindTimeZoneById(invoice.TimeZoneId);

                model.Orders = invoice.Orders.Select(x => PrepareOrderModel(x, timeZoneInfo)).ToList();
                model.OrderIds = String.Join(",", invoice.Orders.Select(o => o.Id));
                var isAdmin = _workContext.CurrentCustomer.IsAdmin();
                model.CanChangeStatus = isAdmin;
                model.CanEditInvoiceNumber = isAdmin;
                model.CanEditCustomer = _permissionService.Authorize(StandardPermissionProvider.ManageCustomers);
                if (model.CanChangeStatus)
                {
                    model.AvailableStatuses = invoice.InvoiceStatus.ToSelectList();
                }

                //var profit = invoice.Orders.Sum(o => o.OrderSubtotalExclTax);
                var tax = invoice.Orders.Sum(o => o.OrderTax);
                var total = invoice.Orders.Sum(o => o.OrderTotal);
                var orderSubtotalExclTax = invoice.Orders.Sum(o => o.OrderSubtotalExclTax);
                var fuelSurcharge = invoice.Orders.Sum(o => o.OrderShippingExclTax);
                //model.AggregatorProfit = _priceFormatter.FormatPrice(profit, true, false);
                model.AggregatorSubtotalExcltax = _priceFormatter.FormatPrice(orderSubtotalExclTax, true, false);
                model.AggregatorFuelSurcharge = _priceFormatter.FormatPrice(fuelSurcharge, true, false);
                model.AggregatorTax = _priceFormatter.FormatPrice(tax, true, false);
                model.AggregatorTotal = _priceFormatter.FormatPrice(total, true, false);

                if (invoice.InvoiceStatus == InvoiceStatus.Published)
                {
                    model.PaymentMethodId = invoice.Customer.GetAttribute<int>(SystemCustomerAttributeNames.PreferredPaymentMethodId);
                    // For no preferred payment customers, set to PaidByCheck
                    var selectedPaymentMethodId = (!model.PaymentMethodId.HasValue || model.PaymentMethodId.Value == 0) ?
                        InvoiceStatus.PaidByCheck : (InvoiceStatus)model.PaymentMethodId;

                    var avaliablePaymentMethods = new List<InvoiceStatus>() { InvoiceStatus.PaidByCheck, InvoiceStatus.PaidByCreditCard };
                    model.AvailablePaymentMethods = avaliablePaymentMethods.Select(x => new SelectListItem()
                        {
                            Text = x.GetLocalizedEnum(_localizationService, _workContext),
                            Value = ((int)x).ToString(),
                            Selected = selectedPaymentMethodId == x
                        }).ToList();
                };
            }
            return model;
        }

        [NonAction]
        protected InvoiceModel CreateInvoiceModel(string selectedIds, DateTime? start, DateTime? end, int? storeId = null)
        {
            var orders = new List<Order>();
            if (selectedIds != null)
            {
                var ids = selectedIds
                    .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(x => Convert.ToInt32(x))
                    .ToArray();
                orders.AddRange(_orderService.GetOrdersByIds(ids));
            }

            var model = new InvoiceModel();

            model.ChargePercentage = 1M;
            model.Discount = 0M;

            // TODO: should support Commerce kind.
            var charge = _invoiceService.GetTotalCharge(orders);
            model.SubTotal = charge.SubTotal;
            model.FuelSurcharge = charge.FuelSurchargeTotal;
            model.Tax = charge.TaxTotal;
            model.Total = charge.Total;

            model.OrderIds = String.Join(",", orders.Select(o => o.Id));
            model.CanChangeStatus = false;
            model.CanEditCustomer = _permissionService.Authorize(StandardPermissionProvider.ManageCustomers);
            model.DueDate = DateTime.Now.AddDays(_invoiceSettings.DueDateInDays);

            string timeZoneId = "";
            if (charge.OrderKind == OrderKind.Commerce)
            {
                int commerceStoreId = storeId ?? 0;
                var store = _storeService.GetStoreById(commerceStoreId);
                if (store == null)
                    throw new ArgumentNullException("store");

                if (orders.Any(o => o.StoreId != commerceStoreId))
                    ErrorNotification(_localizationService.GetResource("Admin.Invoice.OrderList.NotInSameStore"));

                //Commerce orders only need store owner's customer account
                var commerceOrders = new List<Order>();
                var customer = _customerService.GetCustomerById(store.BillToCustomerId);
                //if (customer == null)
                //    throw new ArgumentNullException("customer");

                model.AvailableCustomers = GetAvaliableCustomersFromOrders(commerceOrders, customer);
                timeZoneId = customer == null ? "Pacific Standard Time" : (string.IsNullOrEmpty(customer.TimeZoneId) ? "Pacific Standard Time" : customer.TimeZoneId);

                model.OrderKindId = (int)charge.OrderKind;
                model.ServiceCharge = charge.ServiceChargeTotal;
                model.StorageCharge = charge.StorageChargeTotal;
                model.MonthlyCharge = charge.MonthlyChargeTotal;
                model.TransactionProcessingCharge = charge.TransactionProcessingChargeTotal;
            }
            else
            {
                model.AvailableCustomers = GetAvaliableCustomersFromOrders(orders, null);
                try
                {
                    var firstOrDefault = orders.FirstOrDefault();
                    if (firstOrDefault != null)
                    {
                        if (firstOrDefault.DistrictId == 0)
                        {
                            firstOrDefault.DistrictId = 1;
                        }
                        timeZoneId = _logisticsService.GetDistrictById(firstOrDefault.DistrictId).TimeZoneId;
                    }
                }
                catch
                {
                }
            }

            foreach (var tzi in _dateTimeHelper.GetSystemTimeZones())
                model.AvailableTimeZones.Add(new SelectListItem() { Text = tzi.DisplayName, Value = tzi.Id, Selected = (tzi.Id == timeZoneId) });
            var timeZoneInfo = _dateTimeHelper.FindTimeZoneById(timeZoneId);

            model.Orders = orders.Select(x => PrepareOrderModel(x, timeZoneInfo)).ToList();
            //TODO: localization
            if (start.HasValue && end.HasValue)
            {
                //Name is like "For the Period of: {0} - {1}"
                model.Name = String.Format(_localizationService.GetResource("Admin.Invoice.PeriodInvoiceName"), start.Value.ToShortDateString(), end.Value.ToShortDateString());
            }

            //For create invoice page--show profit, tax and total
            //var profit = orders.Sum(o => o.OrderSubtotalExclTax);
            var tax = orders.Sum(o => o.OrderTax);
            var total = orders.Sum(o => o.OrderTotal);
            var orderSubtotalExclTax = orders.Sum(o => o.OrderSubtotalExclTax);
            var fuelSurcharge = orders.Sum(o => o.OrderShippingExclTax);
            //model.AggregatorProfit = _priceFormatter.FormatPrice(profit, true, false);
            model.AggregatorSubtotalExcltax = _priceFormatter.FormatPrice(orderSubtotalExclTax, true, false);
            model.AggregatorFuelSurcharge = _priceFormatter.FormatPrice(fuelSurcharge, true, false);
            model.AggregatorTax = _priceFormatter.FormatPrice(tax, true, false);
            model.AggregatorTotal = _priceFormatter.FormatPrice(total, true, false);

            // to support invoice creation interaction
            model.StartDate = start;
            model.EndDate = end;
            //get payment collor or action
            model.Payment = "Payment";
            var paymentActionName = "";
            var paymentController = "";
            RouteValueDictionary routeValues;
            var paymentMethod = _paymentService.LoadPaymentMethodBySystemName("Payments.GlobalPayments");
            if (paymentMethod == null)
                throw new Exception("Payment method is null");
            paymentMethod.GetPaymentInfoRoute(out paymentActionName, out paymentController, out routeValues);
            model.PaymentActionName = paymentActionName;
            model.PaymentControllerName = paymentController;
            model.PaymentRoute = routeValues;
            model.PaymentRoute.Add("customerId", _workContext.CurrentCustomer.Id);
            model.PaymentRoute.Add("storeId", 1);
            return model;
        }

        [NonAction]
        protected List<SelectListItem> GetAvaliableCustomersFromOrders(List<Order> orders, Customer customer)
        {
            var customers = orders.Select(o => o.Customer).Distinct().ToList();
            if (customer != null && !customers.Any(x => x.Id == customer.Id))
            {
                customers.Insert(0, customer);
            }

            var csList = customers.Select(c =>
                new SelectListItem()
                {
                    Selected = customer != null && customer.Id == c.Id,
                    Text = String.Format("{0} (ID:{1} Billing: {2})", c.Email, c.Id, c.BillingAddress.Email),
                    Value = c.Id.ToString()
                }).ToList();

            return csList;
        }

        [NonAction]
        protected InvoiceOrderModel PrepareOrderModel(Order order, TimeZoneInfo timeZoneInfo)
        {
            var createdOn = TimeZoneInfo.ConvertTimeFromUtc(order.CreatedOnUtc, timeZoneInfo);
            return new InvoiceOrderModel()
            {
                Id = order.Id,
                OrderTotal = _priceFormatter.FormatPrice(order.OrderTotal, true, false),
                OrderStatus = order.OrderStatus.GetLocalizedEnum(_localizationService, _workContext),
                PaymentStatus = order.PaymentStatus.GetLocalizedEnum(_localizationService, _workContext),
                ShippingStatus = order.ShippingStatus.GetLocalizedEnum(_localizationService, _workContext),
                CustomerEmail = order.BillingAddress.Email,
                CreatedOn = createdOn,
                CreatedOnStr = createdOn.ToString("g")
            };
        }
    }
}
